////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2005.
// -------------------------------------------------------------------------
//  File name:   VoxelObject.h
//  Version:     v1.00
//  Created:     05/02/2005 by Sergiy Shaykin.
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#ifndef __voxelobject_h__
#define __voxelobject_h__

#if _MSC_VER > 1000
#pragma once
#endif

#include "BaseObject.h"

class CEdMesh;

/*!
 *	CTagPoint is an object that represent named 3d position in world.
 *
 */



class CVoxelObject : public CBaseObject
{
public:
	DECLARE_DYNCREATE(CVoxelObject)

	//////////////////////////////////////////////////////////////////////////
	// Ovverides from CBaseObject.
	//////////////////////////////////////////////////////////////////////////
	bool Init( IEditor *ie,CBaseObject *prev,const CString &file );
	void Display( DisplayContext &dc );
	void Draw2dViewportMesh( DisplayContext &dc );

	bool ResetTransformation();
	bool SWCanSplit();
	bool Split();
	void Retriangulate();
	void CopyHM();
	void MakeTerrainHole();

	void GetLocalBounds( AABB &box );

	bool HitTest( HitContext &hc );
	void SetSelected( bool bSelect );
	void UpdateVisibility( bool visible );
	void SetHidden( bool bHidden );
	void SetFrozen( bool bFrozen );
	virtual void SetMinSpec( uint32 nSpec );

	IPhysicalEntity* GetCollisionEntity() const;

	void Serialize( CObjectArchive &ar );

	//! Called when object is being created.
	int MouseCreateCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags );

	IRenderNode* GetEngineNode() const { return m_pRenderNode; };
	float GetLightmapQuality() const { return 1;/*mv_lightmapQuality;*/	}
	void SetLightmapQuality( const f32 fLightmapQuality ) { /*mv_lightmapQuality = fLightmapQuality;*/	}

	//////////////////////////////////////////////////////////////////////////
	//float GetRatioLod() const { return mv_ratioLOD; };
	//float GetRatioViewDist() const { return mv_ratioViewDist; };

	//////////////////////////////////////////////////////////////////////////
	//bool IsRecieveLightmap() const { return mv_recvLightmap; };

	IRenderNode * GetRenderNode(){return m_pRenderNode;};

	//////////////////////////////////////////////////////////////////////////
	// Voxel parameters.
	//////////////////////////////////////////////////////////////////////////
/*	CVariable<float> m_width;
	CVariable<float> m_height;
	CVariable<float> m_length;
	CVariable<float> m_unitSize;*/


	CVariable<bool> mv_outdoor;
	CVariable<bool> mv_goodOccluder;
	CVariable<bool> mv_castShadowMaps;
//	CVariable<bool> mv_castLightmap;
//	CVariable<bool> mv_recvLightmap;
//	CVariable<bool> mv_recvShadowMaps;
	CVariable<bool> mv_hideable;
	CVariable<int> mv_ratioLOD;
	CVariable<int> mv_ratioViewDist;
	CVariable<bool> mv_excludeFromTriangulation;
//	CVariable<float> mv_lightmapQuality;
	CVariable<bool> mv_linkToTerrain;
	CVariable<bool> mv_generateLODs;
	CVariable<bool> mv_computeAO;
	CVariable<bool> mv_snapToTerrain;
//  CVariable<bool> mv_cullTerrainSurface;
	CVariable<bool> mv_smartBaseColor;

protected:
	//! Dtor must be protected.
	CVoxelObject();

	//! Convert ray given in world coordinates to the ray in local Voxel coordinates.
	void DeleteThis() { delete this; };
	void InvalidateTM( int nWhyFlags );


	void BeginEditParams( IEditor *ie,int flags );
	void EndEditParams( IEditor *ie );
	void BeginEditMultiSelParams( bool bAllOfSameType );
	void EndEditMultiSelParams();

	virtual void EnableEditParams(bool bEnable);
	virtual void EnableEditMultiSelParams(bool bEnable, bool bAllOfSameType);

	void OnFileChange( CString filename );

	void UpdateEngineNode( bool bOnlyTransform=false );

	bool CreateGameObject();
	void Done();

	// size of *ppOut must be 2*size+1
	void ConvertDataToString(char** ppOut, void * pData, int size);
	// size of *ppOut must be size
	void StringToData(void ** ppOut, const char * pStr, int size);

	//////////////////////////////////////////////////////////////////////////
	// Local callbacks.

	void OnSizeChange(IVariable *pVar);
	void OnRenderVarChange( IVariable *var );
	//////////////////////////////////////////////////////////////////////////

private:

	AABB m_bbox;
	Matrix34 m_invertTM;

	int m_renderFlags;

	IRenderNode* m_pRenderNode;
	IRenderNode* m_pPrevRenderNode;


friend class CUndoVoxelObject;
friend class CUndoVoxTerrain;
};

/*!
 * Class Description of CVoxelObject.	
 */
class CVoxelObjectClassDesc : public CObjectClassDesc
{
public:
	REFGUID ClassID()
	{
		// {104a3049-7265-45bc-9b6a-4de17fb57a0c}
		static const GUID guid = { 0x104a3049, 0x7265, 0x45bc, { 0x9b, 0x6a, 0x4d, 0xe1, 0x7f, 0xb5, 0x7a, 0x0c } };
		return guid;
	}
	ObjectType GetObjectType() { return OBJTYPE_VOXEL; };
	const char* ClassName() { return "VoxelObject"; };
	const char* Category() { return "Misc"; };
	CRuntimeClass* GetRuntimeClass() { return RUNTIME_CLASS(CVoxelObject); };
	const char* GetFileSpec() { return ""; };
	int GameCreationOrder() { return 150; };
};


//////////////////////////////////////////////////////////////////////////
//! Undo object.
class CUndoVoxelObject : public IUndoObject
{
public:
	CUndoVoxelObject( CVoxelObject * obj)
	{
		m_pObj = obj;
		IVoxelObject * pObject = (IVoxelObject*)(m_pObj->m_pRenderNode);
		if(pObject)
    {
      _smart_ptr<IMemoryBlock> pMB = pObject->GetCompiledData(GetPlatformEndian());
      m_pUndo = new CMemoryBlock;
      m_pUndo->Allocate(pMB->GetSize());
      m_pUndo->Copy(pMB->GetData(), pMB->GetSize());
    }
		m_pRedo = 0;
	}

protected:
	virtual void Release() { delete this; };
	virtual int GetSize() {	return sizeof(*this) + (m_pUndo ? m_pUndo->GetSize() : 0) + (m_pRedo ? m_pRedo->GetSize() : 0); };
	virtual const char* GetDescription() { return "Voxel Object Modify"; };

	virtual void Undo( bool bUndo )
	{
		IVoxelObject * pObject = (IVoxelObject*)(m_pObj->m_pRenderNode);
		if(pObject)
		{
			if (bUndo) // Store for redo.
      {
        _smart_ptr<IMemoryBlock> pMB = pObject->GetCompiledData(GetPlatformEndian());
        m_pRedo = new CMemoryBlock;
        m_pRedo->Allocate(pMB->GetSize());
        m_pRedo->Copy(pMB->GetData(), pMB->GetSize());
      }

			// Restore 
			pObject->SetCompiledData((uint8*)m_pUndo->GetBuffer(),m_pUndo->GetSize(), 0, GetPlatformEndian());
			m_pObj->Retriangulate();
		}
	}
	virtual void Redo()
	{
		IVoxelObject * pObject = (IVoxelObject*)(m_pObj->m_pRenderNode);
		if(pObject)
		{
			if (m_pRedo) // Restore image.
			{
				pObject->SetCompiledData((uint8*)m_pRedo->GetBuffer(),m_pRedo->GetSize(), 0, GetPlatformEndian());
				m_pObj->Retriangulate();
			}
		}
	}

private:
	//IVoxelObject * m_pObject;
	CVoxelObject * m_pObj;
	_smart_ptr<CMemoryBlock> m_pUndo;
	_smart_ptr<CMemoryBlock> m_pRedo;
};

//////////////////////////////////////////////////////////////////////////
//! Undo object.
class CUndoVoxTerrain : public IUndoObject
{
public:
  CUndoVoxTerrain( IVoxTerrain * obj, AABB & areaBox )
  {
    m_pObj = obj;

    m_areaBox = areaBox;

    m_pUndo = m_pObj->GetCompiledData(false, GetPlatformEndian(), true, m_areaBox.IsReset() ? NULL : &m_areaBox);

    m_pRedo = 0;
  }

protected:
  virtual void Release() { delete this; };
  virtual int GetSize() {	return sizeof(*this) + (m_pUndo ? m_pUndo->GetSize() : 0) + (m_pRedo ? m_pRedo->GetSize() : 0); };
  virtual const char* GetDescription() { return "Voxel Terrain Modify"; };

  virtual void Undo( bool bUndo )
  {
    if (bUndo) // Store for redo.
    {
      m_pRedo = m_pObj->GetCompiledData(false, GetPlatformEndian(), true, m_areaBox.IsReset() ? NULL : &m_areaBox);
    }

    // Restore 
    m_pObj->SetCompiledData((uint8*)m_pUndo->GetData(),m_pUndo->GetSize(), true, GetPlatformEndian(), m_areaBox.IsReset() ? NULL : &m_areaBox);
  }
  virtual void Redo()
  {
    if (m_pRedo) // Restore image.
      m_pObj->SetCompiledData((uint8*)m_pRedo->GetData(),m_pRedo->GetSize(), true, GetPlatformEndian(), m_areaBox.IsReset() ? NULL : &m_areaBox);
  }

private:
  //IVoxelObject * m_pObject;
  IVoxTerrain * m_pObj;
  _smart_ptr<IMemoryBlock> m_pUndo;
  _smart_ptr<IMemoryBlock> m_pRedo;
  AABB m_areaBox;
};

#endif // __voxelobject_h__